﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TvdP
{
    public static class Behavior
    {
        public static Delegate FromAction<TTarget>(Action<TTarget, object> initializerAction)
            where TTarget : class
        { return VerifyAction(initializerAction); }

        public static Delegate FromAction<TTgt1, TTgt2>(Action<TTgt1, TTgt2, object> initializerAction)
            where TTgt1 : class
            where TTgt2 : class
        { return VerifyAction(initializerAction); }


        public static Delegate FromAction<TTgt1, TTgt2, TTgt3>(Action<TTgt1, TTgt2, TTgt3, object> initializerAction)
            where TTgt1 : class
            where TTgt2 : class
            where TTgt3 : class
        { return VerifyAction(initializerAction); }


        public static Delegate FromAction<TTgt1, TTgt2, TTgt3, TTgt4>(Action<TTgt1, TTgt2, TTgt3, TTgt4, object> initializerAction)
            where TTgt1 : class
            where TTgt2 : class
            where TTgt3 : class
            where TTgt4 : class
        { return VerifyAction(initializerAction); }

        internal static Delegate VerifyAction(Delegate initializerAction)
        {
            return VerifyBehavior(initializerAction, "initializerAction");
        }

        static void InitializerDelegateException(bool condition, string msg, string parameterName)
        { if (condition) throw new ArgumentException(msg, parameterName); }

        internal static Delegate VerifyBehavior(Delegate initializerDelegate, string parameterName)
        {
            if (initializerDelegate == null)
                throw new ArgumentNullException(parameterName);

            var method = initializerDelegate.Method;

            InitializerDelegateException(method.ReturnType != typeof(void), "Initializer can not have a return value.", parameterName);

            var prmTypes = method.GetParameters().Select(prm => prm.ParameterType).ToArray();

            InitializerDelegateException(prmTypes.Count() < 2, "Initializer must have at least two parameters.", parameterName);
            InitializerDelegateException(prmTypes.Last() != typeof(object), "The last initializer parameter is the 'context' and must be of type 'object'.", parameterName);
            InitializerDelegateException(prmTypes.Any(t => t.IsByRef), "None of the initializer parameters can be passed by reference.", parameterName);
            InitializerDelegateException(prmTypes.Any(t => t.IsValueType), "None of the initializer parameters can be a value type.", parameterName);

            return initializerDelegate;
        }

        public static Delegate FromDelegate(Delegate initializerDelegate)
        {
            return VerifyBehavior(initializerDelegate, "initializerDelegate");
        }
    }
}
